package org.ragdollcat.first.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.ragdollcat.first.constants.UserConstant;
import org.ragdollcat.first.exception.ErrorCode;
import org.ragdollcat.first.exception.ThrowUtils;
import org.ragdollcat.first.mapper.SpaceMapper;
import org.ragdollcat.first.model.dto.picture.PictureUploadRequest;
import org.ragdollcat.first.model.dto.space.SpaceAddRequest;
import org.ragdollcat.first.model.dto.space.SpaceQueryRequest;
import org.ragdollcat.first.model.dto.spaceuser.SpaceUserAddRequest;
import org.ragdollcat.first.model.entity.Picture;
import org.ragdollcat.first.model.entity.Space;
import org.ragdollcat.first.model.entity.SpaceUser;
import org.ragdollcat.first.model.entity.User;
import org.ragdollcat.first.model.enums.SpaceLevelEnum;
import org.ragdollcat.first.model.enums.SpaceRoleEnum;
import org.ragdollcat.first.model.enums.SpaceTypeEnum;
import org.ragdollcat.first.model.vo.SpaceVO;
import org.ragdollcat.first.service.PictureService;
import org.ragdollcat.first.service.SpaceService;
import org.ragdollcat.first.service.SpaceUserService;
import org.ragdollcat.first.service.UserService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 86158
 * @description 针对表【space(空间)】的数据库操作Service实现
 * @createDate 2025-08-30 10:14:36
 */
@Service
public class SpaceServiceImpl extends ServiceImpl<SpaceMapper, Space>
        implements SpaceService {


    @Resource
    private UserService userService;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    @Lazy
    private SpaceUserService spaceUserService;

    @Resource
    @Lazy
    private PictureService pictureService;

    /**
     * 参数校验
     * 新增的时候，空间名称和级别，类型是必填项
     * 修改时，空间名称和级别,类型不是必填项
     *
     * @param
     * @param spaceType
     */
    @Override
    public void vaildUpdSpaceParam(String spaceName, Integer spaceLevel, User loginUser, Integer spaceType, boolean isAdd) {
        ThrowUtils.throwIf(spaceLevel != null && SpaceLevelEnum.getEnumByValue(spaceLevel) == null, ErrorCode.PARAMS_ERROR, "空间级别不合法");
        ThrowUtils.throwIf(spaceLevel != null && SpaceTypeEnum.getEnumByValue(spaceType) == null, ErrorCode.PARAMS_ERROR, "空间类别不合法");
        ThrowUtils.throwIf(StrUtil.isNotBlank(spaceName) && spaceName.length() > 30, ErrorCode.PARAMS_ERROR, "空间名称过长");
        //新增校验
        if (isAdd) {
            ThrowUtils.throwIf(StrUtil.isBlank(spaceName), ErrorCode.PARAMS_ERROR, "空间名称不能为空");
            ThrowUtils.throwIf(spaceLevel == null, ErrorCode.PARAMS_ERROR, "空间级别不能为空");
            ThrowUtils.throwIf(spaceType == null, ErrorCode.PARAMS_ERROR, "空间类别不能为空");
            ThrowUtils.throwIf(!loginUser.getUserRole().equals(UserConstant.ADMIN_ROLE) &&
                            spaceLevel != SpaceLevelEnum.COMMON.getValue(),
                    ErrorCode.OPERATION_ERROR, "目前仅支持开通普通版");
        } else {
            //修改校验
            ThrowUtils.throwIf(spaceType != null &&
                            !loginUser.getUserRole().equals(UserConstant.ADMIN_ROLE),
                    ErrorCode.OPERATION_ERROR, "非管理员无权修改空间类型");
        }
    }

    /**
     * 分页获取空间列表，无需脱敏
     *
     * @param spaceQueryRequest
     * @return
     */
    @Override
    public Page<Space> getSpacePage(SpaceQueryRequest spaceQueryRequest) {
        return page(
                new Page<>(
                        spaceQueryRequest.getCurrent(),
                        spaceQueryRequest.getPageSize()),
                composeSpaceQueryWrapper(spaceQueryRequest));
    }

    /**
     * 组装空间查询条件
     *
     * @param spaceQueryRequest
     * @return
     */
    @Override
    public Wrapper<Space> composeSpaceQueryWrapper(SpaceQueryRequest spaceQueryRequest) {
        QueryWrapper<Space> spaceQueryWrapper = new QueryWrapper<>();
        Long id = spaceQueryRequest.getId();
        Long userId = spaceQueryRequest.getUserId();
        String spaceName = spaceQueryRequest.getSpaceName();
        Integer spaceLevel = spaceQueryRequest.getSpaceLevel();
        String sortField = spaceQueryRequest.getSortField();
        String sortOrder = spaceQueryRequest.getSortOrder();
        Integer spaceType = spaceQueryRequest.getSpaceType();


        spaceQueryWrapper.eq(ObjUtil.isNotEmpty(id), "id", id);
        spaceQueryWrapper.eq(ObjUtil.isNotEmpty(userId), "userId", userId);
        spaceQueryWrapper.like(ObjUtil.isNotEmpty(spaceName), "spaceName", spaceName);
        spaceQueryWrapper.eq(ObjUtil.isNotEmpty(spaceLevel), "spaceLevel", spaceLevel);
        spaceQueryWrapper.eq(ObjUtil.isNotEmpty(spaceType), "spaceType", spaceType);
        spaceQueryWrapper.orderBy(StrUtil.isNotBlank(sortField), sortOrder.equals("ascend"), sortField);
        return spaceQueryWrapper;
    }

    /**
     * 普通用户获取脱敏后的空间分页数据
     *
     * @param spaceQueryRequest
     * @return
     */
    @Override
    public Page<SpaceVO> getSpacePageVO(SpaceQueryRequest spaceQueryRequest) {
        Page<Space> spacePage = page(
                new Page<>(
                        spaceQueryRequest.getCurrent(),
                        spaceQueryRequest.getPageSize()),
                composeSpaceQueryWrapper(spaceQueryRequest));

        //进行脱敏处理
        return castSpacePageToPicPageVO(spacePage);
    }

    /**
     * 将Space对象脱敏
     *
     * @param space
     * @return
     */
    @Override
    public SpaceVO castSpaceToSpaceVO(Space space) {
        SpaceVO spaceVO = SpaceVO.objToVo(space);
        //设置用户
        if (ObjUtil.isNotEmpty(space.getUserId())) {
            spaceVO.setUser(userService.castUserToUserVo(userService.getById(space.getUserId())));
        }
        return spaceVO;
    }

    /**
     * 用户查询图片脱敏，特殊处理PictureVO中的User
     *
     * @param spacePage
     * @return
     */
    private Page<SpaceVO> castSpacePageToPicPageVO(Page<Space> spacePage) {
        Page<SpaceVO> spaceVOPage = new Page<>(spacePage.getCurrent(), spacePage.getSize(), spacePage.getTotal());
        List<Space> pictures = spacePage.getRecords();
        if (CollUtil.isEmpty(pictures)) {
            return spaceVOPage.setRecords(null);
        }

        //利用PictureVO的objToVo方法，将pictures转化为pictureVOS
        List<SpaceVO> spaceVOS = pictures.stream().map(SpaceVO::objToVo).collect(Collectors.toList());

        //获取pictures中的所有userId并且去重
        Set<Long> userIds = pictures.stream().map(Space::getUserId).collect(Collectors.toSet());
        //批量查询用户表
        List<User> users = userService.listByIds(userIds);


        //users 的list转map key是用户id，value是对应的用户
        Map<Long, User> userMap = users.stream()
                .collect(Collectors.toMap(
                        User::getId,
                        Function.identity(),
                        (existingValue, newValue) -> newValue  // 第三个参数：重复键时的合并规则
                ));

        //填充user,处理tags
        for (SpaceVO spaceVO : spaceVOS) {
            Long userId = spaceVO.getUserId();
            User user = null;
            if (userMap.containsKey(userId)) {
                user = userMap.get(userId);
            }
            //user对象还需要再次转成userVO
            spaceVO.setUser(userService.castUserToUserVo(user));
        }

        return spaceVOPage.setRecords(spaceVOS);
    }

    /**
     * 根据空间级别，填充限额和大小
     *
     * @param space
     */
    @Override
    public void fillSpaceParams(Space space) {
        Integer spaceLevel = space.getSpaceLevel();

        SpaceLevelEnum spaceLevelEnum = SpaceLevelEnum.getEnumByValue(spaceLevel);
        space.setMaxCount(space.getMaxCount() == null ? Objects.requireNonNull(spaceLevelEnum).getMaxCount() : space.getMaxCount());
        space.setMaxSize(space.getMaxSize() == null ? Objects.requireNonNull(spaceLevelEnum).getMaxSize() : space.getMaxSize());
    }

    /**
     * 管理员或用户创建空间
     * 需要保证每个用户只能创建一个自己的空间
     * 锁 + 编程式事务
     *
     * @param spaceAddRequest
     * @param httpServletRequest
     * @return
     */
    @Override
    public Long createSpace(SpaceAddRequest spaceAddRequest, HttpServletRequest httpServletRequest) {
        User loginUser = userService.getLoginUser(httpServletRequest);
        //参数校验
        this.vaildUpdSpaceParam(spaceAddRequest.getSpaceName(), spaceAddRequest.getSpaceLevel(), loginUser, spaceAddRequest.getSpaceType(), true);
        //填充默认值
        Space space = new Space();
        //填充默认名称和级别，类型
        space.setSpaceLevel(spaceAddRequest.getSpaceLevel() == null ? SpaceLevelEnum.COMMON.getValue() : spaceAddRequest.getSpaceLevel());
        space.setSpaceName(spaceAddRequest.getSpaceName() == null ? "默认空间" : spaceAddRequest.getSpaceName());
        space.setSpaceLevel(spaceAddRequest.getSpaceType() == null ? SpaceTypeEnum.PRIVATE.getValue() : spaceAddRequest.getSpaceType());
        //填充该级别下的限额和大小
        this.fillSpaceParams(space);

        //控制每个用户ID只能创建一个空间
        Long id = loginUser.getId();
        Integer spaceType = spaceAddRequest.getSpaceType();
        space.setSpaceType(spaceType);
        space.setUserId(id);

        //加分布式锁
        Long result = null;
        RLock lock = redissonClient.getLock(String.valueOf(id).intern());
        try {
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                //手动开启事务
                result = transactionTemplate.execute((status) -> {
                    ThrowUtils.throwIf(lambdaQuery().eq(Space::getSpaceType, spaceType).eq(Space::getUserId, id).exists(), ErrorCode.OPERATION_ERROR, "每个用户只能创建一种空间");
                    ThrowUtils.throwIf(!save(space), ErrorCode.OPERATION_ERROR, "空间创建失败");
                    //创建团队空间，自己默认是管理员
                    if (spaceType.equals(SpaceTypeEnum.TEAM.getValue())) {
                        SpaceUser spaceUser = new SpaceUser();
                        spaceUser.setUserId(id);
                        spaceUser.setSpaceId(space.getId());
                        spaceUser.setSpaceRole(SpaceRoleEnum.ADMIN.getValue());
                        ThrowUtils.throwIf(!spaceUserService.save(spaceUser), ErrorCode.OPERATION_ERROR, "创建团队成员记录失败");
                    }
                    return space.getId();
                });

            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
        return result == null ? -1 : result;
    }

    /**
     * 校验上传的空间
     * 公共图库是没有空间ID的
     * 每个人只能往自己的空间传图片
     *
     * @param pictureUploadRequest
     * @param user
     */
    @Override
    public Long vaildUploadSpace(PictureUploadRequest pictureUploadRequest, User user) {
        Long id = pictureUploadRequest.getId();
        Long spaceId = pictureUploadRequest.getSpaceId();
        Space space = getById(spaceId);
        //新增时的校验
        if (id == null) {
            if (spaceId == null) {
                return null;
            }
            ThrowUtils.throwIf(ObjUtil.isEmpty(space), ErrorCode.PARAMS_ERROR, "空间不存在！");
            ThrowUtils.throwIf(!space.getUserId().equals(user.getId()), ErrorCode.OPERATION_ERROR, "无权操作！");
        }//修改时的校验
        //如果当前的spaceId为空，就设置spaceId为旧图片的spaceId
        else {
            Picture picture = pictureService.getById(id);
            if (spaceId == null) {
                spaceId = picture.getSpaceId();
            } else {
                //当前的spaceId不为空，并且和旧图的spaceId不一致，就抛出异常
                ThrowUtils.throwIf(!spaceId.equals(picture.getSpaceId()), ErrorCode.OPERATION_ERROR, "空间ID不一致");
            }
        }
        //还要校验限额
        ThrowUtils.throwIf(space.getTotalCount() >= space.getMaxCount(), ErrorCode.OPERATION_ERROR, "空间条数不足");
        ThrowUtils.throwIf(space.getTotalSize() >= space.getMaxSize(), ErrorCode.OPERATION_ERROR, "空间大小不足");
        return spaceId;
    }
}




